import os
import re
import shutil, logging

from datetime import datetime

from concurrent.futures import ThreadPoolExecutor, Future
from logging import exception
from threading import Lock
from flask import Flask, request, jsonify, current_app
from flask_cors import CORS

from sqlalchemy.exc import SQLAlchemyError

from flask_sqlalchemy import SQLAlchemy

import config

from config import BACKEND_ROOT, FRONTEND_VIDEO_PATH


app = Flask(__name__)
db = SQLAlchemy()
CORS(app)
app.config.from_object(config)
db.init_app(app)
logging.basicConfig(level=logging.DEBUG)


class VideoFolder(db.Model):
    __tablename__ = 'video_folder'  # 修正：引号和下划线
    id = db.Column(db.Integer, primary_key=True, autoincrement=True, unique=True)
    folder_name = db.Column(db.String(200), nullable=False)
    server_path = db.Column(db.String(200), nullable=False)  # 修正：db.String
    frontend_path = db.Column(db.String(300), nullable=False)  # 修正：db.String
    upload_time = db.Column(db.DateTime, default=datetime.now())  # 注意 default=datetime.now 的行为
    is_copied = db.Column(db.Boolean, default=False)


class AnomalyRecord(db.Model):
    __tablename__ = 'anomaly'  # 修正：去掉前导下划线
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    number = db.Column(db.String(200), nullable=False)
    time = db.Column(db.DateTime, default=datetime.now())  # 注意 default=datetime.now 的行为
    lng = db.Column(db.Float, nullable=False)
    lat = db.Column(db.Float, nullable=False)
    hight = db.Column(db.Float, nullable=False)  # 保持原拼写


app.config['PER_PAGE'] = 20
cpu_count = os.cpu_count() or 4
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

max_workers = cpu_count * 5


class MonitoredThreadPoolExecutor(ThreadPoolExecutor):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.active_task = 0
        self.lock = Lock()

    def submit(self, fn, *args, **kwargs) -> Future:
        with self.lock:
            self.active_task += 1
            if self.active_task >= max_workers * 0.8:
                logging.warning(f'任务数过多：{self.active_task}/{self._max_workers},可能导致延迟')
        future = super().submit(fn, *args, **kwargs)

    def task_done_callback(self, future):
        with self.lock:
            self.active_task -= 1
        future.add_done_callback(self.task_done_callback)
        return future


monitored_executor = MonitoredThreadPoolExecutor(max_workers=cpu_count * 5)


def init_video_folder():  # 初始化视频文件夹
    frontend_path = app.config['FRONTEND_VIDEO_PATH']
    if not os.path.exists(frontend_path):
        os.makedirs(frontend_path, exist_ok=True)
        logging.info(f'目录创建成功:{frontend_path}')
    else:
        logging.info(f'目录已创建:{frontend_path}')


def init_database():  # 初始化数据库表
    logging.info('开始初始化数据库')
    try:
        db.create_all()

        logging.info('数据库初始化成功')
    except SQLAlchemyError as e:
        logging.error(f'数据库初始化失败{str(e)}')
        raise


def copy_to_frontend(folder):  # 复制后端视频文件到前端视频文件夹

    src = os.path.join(BACKEND_ROOT, folder.folder_name)

    dst = os.path.join(app.config['FRONTEND_VIDEO_PATH'], folder.folder_name)

    future = do_copy( src, dst, folder)

    return future


def do_copy(src, dst, folder):  # 实现复制操作
    try:
        print(f'检查路径安全性：{dst}')
        if not is_safe_path(dst, app.config['FRONTEND_VIDEO_PATH']):
            print(f'路径不安全，跳过复制：{dst}')
            raise ValueError('非法路径：{}'.format(dst))
        else:
            print(f'路径安全，继续复制：{dst}')


            print(f'源文件夹路径：{src}')
            print(f'目标路径：{dst}')
            if not os.path.exists(src):
                print(f'源文件夹不存在：{src}')
                return{'status':'failed','folder':folder.folder_name,'error':'源文件夹不存在'}
            try:

                 print(f'开始复制：从{src}到{dst}')
                 shutil.copy2(src, dst)
                 print(f'复制完成:{dst}')
                 folder.is_copied = True
                 db.session.add(folder)
                 logging.info(f'文件复制成功：{src}——>{dst}')
                 result = {'status''folder': folder.folder_name}
                 db.session.commit()
                 return result
            except Exception as e:
                print('copy2执行失败')
                db.session.rollback()
                return{'status':'failed','folder':folder.folder_name,'error':str(e)}
    except Exception as e:
        db.session.rollback()
        print(f'文件复制失败：{src}-->{dst},错误:{str(e)}')
        if os.path.exists(dst):
            shutil.rmtree(dst, ignore_errors=True)
        return {'status': 'failed', 'folder': folder.folder_name, 'error': str(e)}


def is_safe_path(path: str, base_dir: str) -> bool:
    if '../' in path or re.search(r'\.\.[/\\]', path):
        return False
    dangerous_chars = r'[|;$&<>(){}[\]'"@]"
    if re.search(dangerous_chars, path):
        return False
    real_path = os.path.abspath(path)
    real_base = os.path.abspath(base_dir)
    return real_path.startswith(real_base)


def table_attributes_to_txt():  # 生成txt文本
    try:
        all_records = AnomalyRecord.query.all()
        if not all_records:
            app.logger.info('数据库表中无记录，无需导出')
            return
        for record in all_records:
            if not hasattr(record, 'time') or record.time is None:
                app.logger.warning('记录ID{record.id}无有效time字段，跳过')
            else:
                time_str = record.time.strftime('%Y-%m-%d %H-%M-%S')
                file_name = f'{time_str}_record.txt'
                content = [
                    f'时间：{record.time}',
                    f'定位经度{record.lng}',
                    f'定位纬度{record.lat}',
                    f'定位高度{record.hight}',
                    f'异常类型{record.number}'
                ]

                file_path = os.path.join(FRONTEND_VIDEO_PATH, file_name)
                with open(file_path, 'w', encoding='utf-8') as file:
                    file.write('\n'.join(content))
                    app.logger.info(f' 成功导出记录{record.id}为{file_name}')
    except SQLAlchemyError as e:
        app.logger.info(f'数据库查询失败：{str(e)}', exc_info=True)
    except Exception as e:
        app.logger.info(f'导出文件失败{str(e)}', exc_info=True)


def get_video_from_folder(folder_path):  # 提取视频的信息
    video_suffix = ('.mp4', 'avi','mkv','mov')
    video_list = []
    for root, dirs, files in os.walk(folder_path):
        for file in files:
            video_name = ''
            video_path = ''
            frontend_path = ''
            upload_time = datetime.now()
            if file.lower().endswith(video_suffix):
                video_path = os.path.join(root, file)
                video_name = file
                try:
                    stat_info = os.stat(video_path)
                    frontend_path = os.path.join(app.config['FRONTEND_VIDEO_PATH'], video_name)
                    if os.name == 'nt':
                        create_time = stat_info.st_mtime
                    else:
                        create_time = stat_info.st_birthtime
                    upload_time = datetime.fromtimestamp(create_time)
                except OSError as e:
                    print(f'处理文件{file}时出现异常：{str(e)}')
            if video_name:
             video_list.append({
                'folder_name': video_name,
                'server_path': video_path,
                'upload_time': upload_time,
                'frontend_path': frontend_path

            })

    return video_list


def save_video_to_db(video_list):  # 存入数据库
    for info in video_list:
        existing_videos = VideoFolder.query.filter_by(folder_name=info['folder_name']).first()
        if not existing_videos:
            new_video = VideoFolder(**info)
            db.session.add(new_video)
    db.session.commit()


def auto_sync_video():
    FIXED_VIDEO_PATH = app.config.get('BACKEND_ROOT')
    if not FIXED_VIDEO_PATH or not os.path.exists(FIXED_VIDEO_PATH):
        print(f'提示：视频文件不合法（路径{FIXED_VIDEO_PATH}')
        return
    video_list = get_video_from_folder(FIXED_VIDEO_PATH)
    if not video_list:
        print(f'提示：未找到视频文件{FIXED_VIDEO_PATH}')
        return
    save_video_to_db(video_list)
    print(f'视频同步完成，共{len(video_list)}个视频存入数据库中')


@app.route('/latest_videos', methods=['GET'])  # 给前端最新视频
def latest_videos():
    try:

        page = request.args.get('page', 1, type=int)

        per_page = app.config.get('PER_PAGE', 20)

        new_folders_query = VideoFolder.query.filter_by(is_copied=False).order_by(VideoFolder.id)

        new_folders = new_folders_query.paginate(page=page, per_page=per_page, error_out=False)

        for folder in new_folders.items:
            copy_result=copy_to_frontend(folder)
            print(copy_result)
            if copy_result is None:
                logging.error(f'复制文件夹{folder.folder_name}时，copy_to_frontend返回None')
            if copy_result['status'] !='success':
                logging.error(f"复制文件夹{folder.folder_name}失败：{copy_result['error']}")

        try:
            latest = VideoFolder.query.filter_by(is_copied=True).order_by(VideoFolder.upload_time.desc()).all()

            logging.debug('latest的类型是：%s', type(latest))
        except SQLAlchemyError as e:
            return jsonify({
                'status': 'error',
                'message': '数据库查询失败',
                'error': str(e)
            }), 500
        return jsonify({
            'status': 'success',
            'count': len(latest),
            'video': [f.folder_name for f in latest],
            'page_info': {
                'current_page': new_folders.page,

                'total_pages': new_folders.pages,  # 正确的属性名
                'total_items': new_folders.total,  # 正确的属性名
            }
        })
    except Exception as e:
        print('数据库查询失败', str(e))
        return (jsonify({
            'status': 'error',
            'message': '内部错误',
            'error': str(e),

        }), 500)


def print_routes():
    print('已注册路由：')
    for route in app.url_map.iter_rules():
        print(f'{route.rule}->{route.endpoint}')


# 新增接口：获取异常记录（定位经纬度和异常类型）
@app.route('/anomaly_data', methods=['GET'])
def get_anomaly_data():
    try:
        # 查询所有异常记录并按时间降序排列
        all_records = AnomalyRecord.query.order_by(AnomalyRecord.time.desc()).all()

        # 构建返回数据
        records_data = []
        for record in all_records:
            records_data.append({
                'id': record.id,
                'time': record.time.strftime('%Y-%m-%d %H:%M:%S') if record.time else None,
                'lng': record.lng,  # 经度
                'lat': record.lat,  # 纬度
                'hight': record.hight,  # 高度
                'number': record.number  # 异常类型
            })

        return jsonify({
            'status': 'success',
            'records': records_data,
            'total_records': len(all_records)
        })

    except SQLAlchemyError as e:
        logging.error(f'数据库查询失败: {str(e)}')
        return jsonify({
            'status': 'error',
            'message': '数据库查询失败',
            'error': str(e)
        }), 500

    except Exception as e:
        logging.error(f'获取异常数据失败: {str(e)}')
        return jsonify({
            'status': 'error',
            'message': '内部服务器错误',
            'error': str(e)
        }), 500


if __name__ == '__main__':
    with app.app_context():
        init_database()
        init_video_folder()
        print_routes()
        auto_sync_video()
        table_attributes_to_txt()
    app.run(host='0.0.0.0', port=5002, debug=True,use_reloader=False)
